# Hunger Games Competition, Brilliant.org
# Joshua N Miller
# jnmiller15@gmail.com


"""The underlying concept is that we do not focus on individuals but the community as a whole.
    By focusing on an average reputation we can determine if there is correlation to the m. 
    This allows us to dictate how many hunts and slacks we will do for the round. The algorithm
    makes minor adjustments in the direction it feels necessary to get a streak going. A streak
    is determined from the accuracy of our decisions. There is also an average streak being
    generated every round to help determine whether adjustments are really needed."""

m_threshold_reached = [1]       #variables counts if m was reached
streaks = [0]                   #holds streak sizes
players_hunted_per_round = []   #Not used
hot_streak = []                 #counts hot streaks
bad_streak = []                 #counts bad streaks
prev_decisions = []             #holds my previous decisions
prev_opp_decisions = []         #holds previous opp decisions
accuracies = []                 #holds accuracies for every round
MINOR_ADJUSTMENT = 0.05         #minor adjustment will be manipulated
MAJOR_ADJUSTMENT = 0.10         #major adjustment will be manipulated

def hunt_choices(round_number, current_food, current_reputation, m,  player_reputations):
    
    hunt_decisions = strategy(round_number, player_reputations)
    set_prev_decisions(hunt_decisions)
    return hunt_decisions

def hunt_outcomes(food_earnings):

    set_prev_opp_decisions(food_earnings)
    set_accuracies()
    set_streak()

def round_end(award, m, number_hunters):

    set_m_threshold_reached(check_m_threshold_reached(award))
    set_players_hunted_per_round(number_hunters)





def mean_player_average(reputations): #returns an average of all player reps

    average = 0 
    
    for rep in reputations:
        average += rep
    
    average = average / len(reputations)
    
    return average

def set_players_hunted_per_round(num_hunters):#not used
        players_hunted_per_round.append(num_hunters)
def get_players_hunted_per_round():#not used
        return players_hunted_per_round

def check_m_threshold_reached(award): #checks if m threshold was reached
    if award > 0:
        return 1
    elif award < 0:
        return 0

def set_m_threshold_reached(num): #sets global variable m_threshold_reached
        m_threshold_reached.append(num)
def get_m_threshold_reached():#returns list of reached thresholds indexed by round
        return m_threshold_reached



#Accuracy is based solely on decisions
def set_accuracies():
    total_decisions = len(get_prev_decisions())
    decisions_correct = 0.0
    counter = 0
    for decision in get_prev_decisions():
        if decision == get_prev_opp_decisions()[counter]:
            decisions_correct += 1
        counter +=1
    
    num = decisions_correct/total_decisions
    accuracies.append(num)#compares decisions and sets accuracies
    
def get_accuracies():#returns accuracies
    return accuracies
def set_prev_decisions(decisions):
    del prev_decisions[:]
    for i in decisions:
        prev_decisions.append(i)#sets prev decisions
    
def get_prev_decisions():#returns prev decisions
    return prev_decisions


def set_prev_opp_decisions(food_earned):
    del prev_opp_decisions[:] 
    for food in food_earned:
        if food == 0 or food ==1:
            prev_opp_decisions.append('h')
        elif food == -2 or food == -3:
            prev_opp_decisions.append('s')#sets prev opponent decisions
           
def get_prev_opp_decisions(): #returns prev opponent decisions
    return prev_opp_decisions




#If accuracy goes below the last rounds accuracy then streak ends or continues
#If accuracy goes over then streak start or continues
#Also appends streak into list of streaks to average later
def set_streak():
    length = len(get_accuracies())
    if length > 1:
        if accuracies[-1] >= accuracies[-2]: 
            if get_streak() >= 0:                                           
                streaks[-1] += 1
            else:
                streak.append(1)
        else:
            if get_streak() <= 0:
                streaks[-1] -= 1
            else:
                streaks[-1] = 0
    else:
        if accuracies[-1] >= 0:
            streaks[-1] += 1
        else:
            streaks[-1] -= 1
            
    set_streaks_averages()

def get_streak():#returns the last streak in streaks
    return streaks[-1]
def get_streaks():#returns list of streaks
    return streaks

def set_streaks_averages():
    
    del hot_streak[:]
    del bad_streak[:]
    for s in get_streaks():
        if s > 0:
            hot_streak.append(s)
        else:
            bad_streak.append(s)#makes bad and hot streaks
    
def get_bad_streak_average():
    length = len(bad_streak)
    total = 0
    for bad in bad_streak:
        total += bad
    if total == 0:
        return 0;
    else:
        average = total / length
        return average#returns bad streak average
    
    
def get_hot_streak_average():
    length = len(hot_streak)
    total = 0
    for hot in hot_streak:
        total += hot
    if total == 0:
        return 0;
    else:
        average = total / length
        return average#returns hot streak average

def adjustment_direction(round_number):
    hunt_count = 0
    slack_count = 0
    adjustment = 0
    if round_number == 1:
        return 1
    else:
        for decision in get_prev_decisions():
            if decision == "h":
                hunt_count +=1
            else:
                slack_count +=1
        if streaks[-1] > 0 and hunt_count > slack_count:
            adjustment = -1 * get_streak() 
            return adjustment
        elif streaks[-1] > 0 and slack_count > hunt_count:
            adjustment = 1 * get_streak() 
            return adjustment
        elif streak[-1] < 0 and hunt_count > slack_count:
            adjustment = 1 * get_streak() 
            return adjustment
        else:
            adjustment = -1 * get_streak() 
            return adjustment#returns adjustment size and direction
            
#streaks, streaks, streaks!!!
def strategy(round_number, player_reputations):
    
    streak = get_streak()
    
    if m_threshold_reached[-1]==1 or round_number == 1:
        if streak >= 0 and streak >= get_hot_streak_average():
            return hunts(player_reputations, 0)#no adjustment
    
        elif streak >= 0 and streak < get_hot_streak_average():
            return hunts(player_reputations,MINOR_ADJUSTMENT*adjustment_direction(round_number))#minor adjustment to opposite direction
        
        elif streak < 0 and streak > get_bad_streak_average():
            return hunts(player_reputations,MINOR_ADJUSTMENT*adjustment_direction(round_number))#minor adjustment to opposite direction
    
        elif streak < 0 and streak < get_bad_streak_average():
            return hunts(player_reputations,MAJOR_ADJUSTMENT*adjustment_direction(round_number))#large adjustment opposite direction
    else:
        if streak >= 0 and streak >= get_hot_streak_average():
            return hunts(player_reputations, 0)#no adjustment
    
        elif streak >= 0 and streak < get_hot_streak_average():
            return hunts(player_reputations,MINOR_ADJUSTMENT*adjustment_direction(round_number))#minor adjustment to opposite direction
    
        elif streak < 0 and streak > get_bad_streak_average():
            return hunts(player_reputations,MAJOR_ADJUSTMENT*adjustment_direction(round_number))#minor adjustment to opposite direction
    
        elif streak < 0 and streak < get_bad_streak_average():
            return hunts(player_reputations,1)#large adjustment opposite direction#checks m threshold

def hunts(player_reputations, adjustment):#returns decisions by checking reps + adjustment
    decisions = []
    average_opp_rep = mean_player_average(player_reputations)
    counter = 0
    for rep in player_reputations:
        if rep >= average_opp_rep + adjustment:
            decisions.append('h')
        elif rep < average_opp_rep + adjustment:
            decisions.append('s')

    return decisions
